package com.may.wind.filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.may.wind.encrypt.util.AesUtil;
import com.may.wind.encrypt.util.RsaUtil;
import com.may.wind.exception.VerifyCodeException;
import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class LoginFilter extends AbstractAuthenticationProcessingFilter {
    private final Logger logger = LoggerFactory.getLogger(LoginFilter.class);

    public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";

    public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";

    /**
     * 登录拦截请求
     */
    private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER = new AntPathRequestMatcher("/api/login",
            "POST");

    private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;

    private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;

    private boolean postOnly = true;

    public LoginFilter() {
        super(DEFAULT_ANT_PATH_REQUEST_MATCHER);
    }

    public LoginFilter(AuthenticationManager authenticationManager) {
        super(DEFAULT_ANT_PATH_REQUEST_MATCHER, authenticationManager);

    }

    /**
     * @param request
     * @param response
     * @return
     * @throws AuthenticationException
     * @describe 重写attemptAuthentication方法，因为前端使用axios发送ajax请求，通过fastJson读取request流中的登录信息，
     * 后续步骤同其父类类似，因为实现了RememberMe功能，而在后面的AbstractRememberMeServices中是通过request.getParameter(parameter)获取记住我参数，
     * 而我们在后端不好对request的parameter进行手动添加参数，因此我们使用request.setAttribute("remember-me",rememberMe);进行传参，后面通过重写AbstractRememberMeServices
     * 的rememberMeRequested(HttpServletRequest request, String parameter)方法来接收参数
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        if (this.postOnly && !request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        }

        UsernamePasswordAuthenticationToken authRequest = null;
        try {
            //AES加密后的数据
            String data = request.getParameter("data");
            //后端RSA公钥加密后的AES的key
            String aesKey = request.getParameter("aesKey");
            //前端公钥
            String publicKey = request.getParameter("publicKey");
            logger.error("前端公钥：" + publicKey);
            //后端私钥解密的到AES的key
            byte[] plaintext = RsaUtil.decryptByPrivateKey(Base64.decodeBase64(aesKey), RsaUtil.getPrivateKey());
            aesKey = new String(plaintext);
            logger.error("解密出来的AES的key：" + aesKey);

            //AES解密得到明文data数据
            String decrypt = AesUtil.decrypt(data, aesKey);
            logger.error("解密出来的data数据：" + decrypt);

            JSONObject jsonObject = JSON.parseObject(decrypt);
            String verifyCode = jsonObject.getString("verifyCode");//用户传来的验证码
            if (verifyCode == null) {
                verifyCode = "";
            }
            validateVerify(verifyCode, request);//校验验证是否正确
            String username = jsonObject.getString("username");//获取用户名
            String password = jsonObject.getString("password");//密码
            String rememberMe = jsonObject.getString("rememberMe");//记住我
            username = username.trim();
            request.setAttribute("remember-me", rememberMe);//设置rememberMe
            authRequest = new UsernamePasswordAuthenticationToken(username, password);
        } catch (Exception exception) {
            exception.printStackTrace();
            logger.error(exception.toString());
        }

        // Allow subclasses to set the "details" property
        setDetails(request, authRequest);
        return this.getAuthenticationManager().authenticate(authRequest);
    }

    protected void setDetails(HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest) {
        authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
    }

    private boolean validateVerify(String inputVerify, HttpServletRequest request) {
        // 获取当前线程绑定的request对象
        // HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        // 不分区大小写
        // 这个validateCode是在servlet中存入session的名字
        String validateCode = ((String) request.getSession().getAttribute("validateCode")).toLowerCase();
        inputVerify = inputVerify.toLowerCase();

        logger.error("验证码：" + validateCode + "，用户输入：" + inputVerify);
        if (validateCode.equals(inputVerify)) {
            return true;
        } else {
            throw new VerifyCodeException("验证码输入错误");
        }
    }

    /**
     * 获取HttpServletRequest中的Json数据
     *
     * @param request
     * @return
     */
    private String getJsonParam(HttpServletRequest request) {
        String jsonParam = "";
        ServletInputStream inputStream = null;
        try {
            int contentLength = request.getContentLength();
            if (!(contentLength < 0)) {
                byte[] buffer = new byte[contentLength];
                inputStream = request.getInputStream();
                for (int i = 0; i < contentLength; ) {
                    int len = inputStream.read(buffer, i, contentLength);
                    if (len == -1) {
                        break;
                    }
                    i += len;
                }
                jsonParam = new String(buffer, "utf-8");
            }
        } catch (IOException exception) {
            logger.error("参数转换成json异常g{}", exception);
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException exception) {
                    logger.error("参数转换成json异常s{}", exception);
                }
            }
        }
        return jsonParam;
    }
}
